extern "C" 
{
	#include "lua.h"
	#include "lualib.h"
	#include "lauxlib.h"
};

#include <boost/bind.hpp>
#include <boost/regex.hpp>

#include "tinyxml.h"

#include "lua_tinker.h"
#include "common.h"
#include "actor.h"

#include "asset.h"
#include "mesh.h"
#include "effect.h"
#include "body.h"

#include "collada.h"
#include "colladawrapper.h"

#include "actormanager.h"

std::map< std::string, ActorTypeInfo* > ActorManager::_types;

static bool Match( const boost::cmatch& match, std::vector< std::string >* vec )
{
	for( size_t i = 1; i < match.size(); ++i )
	{
		vec->push_back( match.str( static_cast< int >( i ) ) );
	}
	return true;
}

void ActorManager::Register( const std::string& typeName, const std::string& filePath )
{
	TypeListIter it = _types.find( typeName );
	if( it != _types.end() )
	{
		return;
	}

	FILE* fp;
	fopen_s( &fp, filePath.c_str(), "r" );
	if( fp == NULL )
	{
		assert( false && "File not exists!" );
	}
	fclose( fp );

	ActorTypeInfo* pInfo = new ActorTypeInfo;
	pInfo->L		= NULL;
	pInfo->body		= NULL;
	_types[ typeName ] = pInfo;

	lua_State* L = lua_open();
	luaopen_base( L );
	luaopen_string( L );

	lua_tinker::def( L, "Param", &ActorManager::SetParameter );
	lua_tinker::def( L, "Func", &ActorManager::SetFunction );
	lua_tinker::def( L, "Body", &ActorManager::SetBody );
	lua_tinker::def( L, "CommonFX", &ActorManager::SetCommonEffect );
	lua_tinker::def( L, "CommonMaterial", &ActorManager::SetCommonMaterial );

	lua_tinker::class_add< D3DXVECTOR2 >( L, "float2" );
	lua_tinker::class_con< D3DXVECTOR2 >( L, lua_tinker::constructor< D3DXVECTOR2, float, float > );
	lua_tinker::class_add< D3DXVECTOR3 >( L, "float3" );
	lua_tinker::class_con< D3DXVECTOR3 >( L, lua_tinker::constructor< D3DXVECTOR3, float, float, float > );

	lua_tinker::dofile( L, filePath.c_str() );

	pInfo->L = L;
}

void ActorManager::SetCommonEffect( const char* type, const char* path )
{
	TiXmlDocument doc;
	doc.LoadFile( path );

	if( doc.Error() )
	{
		assert( false && "Wrong fx description file format!" );
	}

	TiXmlElement* root = doc.FirstChildElement();
	const char* fxPath = root->Attribute( "url" );
	if( fxPath == NULL )
	{
		assert( false && "Wrong fx description file format!" );
	}
	
	std::string fxName = std::string( type ) + std::string( "_commonFx" );
	Effect* pEffect = Effect::Create( fxName, fxPath );
	if( pEffect == NULL )
	{
		assert( false );
	}

	TiXmlElement* c = root->FirstChildElement( "constants" )->FirstChildElement();
	for( ; c; c = c->NextSiblingElement() )
	{
		const char* type = c->Attribute( "type" );
		const char* name = c->Attribute( "name" );
		pEffect->SetConstant( name, type );
	}

	TiXmlElement* t = root->FirstChildElement( "techniques" )->FirstChildElement();
	for( ; t; t = t->NextSiblingElement() )
	{
		const char* techName = t->Attribute( "name" );
		pEffect->SetTechniuqe( techName );
		TiXmlElement* p = t->FirstChildElement();
		for( ; p; p = p->NextSiblingElement() )
		{
			const char* passName = p->GetText();
			pEffect->SetPass( techName, passName );
		}
	}

	_types[ type ]->body->SetCommonEffect( pEffect );
}

void ActorManager::SetCommonMaterial( const char* type )
{
}

void ActorManager::SetFunction( const char* format )
{
	boost::regex ex( "([_a-zA-Z]\\w*\\*?)[ \\t]+([_a-zA-Z]\\w*)::([_a-zA-Z]\\w*)\\([ \\t]*([ \\t\\w,\\*]*?)[ \\t]*\\)" );
	boost::cmatch match;
	if( boost::regex_search( format, match, ex ) )
	{
		const std::string& typeName		= match.str( 2 );
		const std::string& funcName		= match.str( 3 );
		const std::string& returnType	= match.str( 1 );
		const std::string& paramStr		= match.str( 4 );

		std::vector< std::string > params;
		boost::regex ex2( "([_a-zA-z]\\w*\\*?)" );
		int nParam = boost::regex_grep( boost::bind( Match, _1, &params ), paramStr.c_str(), ex2 ) ;

		_types[ typeName ]->funcs[ funcName ].push_back( returnType );
		for( int i = 0; i < nParam; ++i )
		{
			_types[ typeName ]->funcs[ funcName ].push_back( params[i] );
		}
	}	
}

void ActorManager::SetParameter( const char* format )
{
	boost::regex ex( "([_a-zA-Z]\\w*\\*?)[ \\t]+([_a-zA-Z]\\w*)::([_a-zA-Z]\\w*)" );
	boost::cmatch match;
	if( boost::regex_search( format, match, ex ) )
	{
		const std::string& typeName		= match.str( 2 );
		const std::string& paramName	= match.str( 3 );
		const std::string& type			= match.str( 1 );

		_types[ typeName ]->params[ paramName ] = type;
	}
}

void ActorManager::SetBody( const char* type, const char* path )
{
	boost::regex ex( "[_\\.\\w]+\\.([\\w]+)" );
	boost::cmatch match;
	if( boost::regex_search( path, match, ex ) )
	{
		const std::string& extension = match.str( 1 );
		
		if( extension == "dae" )
		{
			Collada::Data* pData = Collada::LoadFromFile( path );

			// Register assets to asset manager
			ColladaWrapper::RegisterAssets( pData );

			// Get body infomations
			Body* pBody = Body::Create( type );
			if( pBody == NULL )
			{
				assert( false );
			}
			ColladaWrapper::GetBodyInfos( *pBody, pData );
			_types[ type ]->body = pBody;

			delete pData;
		}

	}
}

void ActorManager::Unregister( const std::string& typeName )
{
	TypeListIter it = _types.find( typeName );
	if( it == _types.end() )
	{
		return;
	}

	delete it->second;
	_types.erase( it );
}

void ActorManager::UnregisterAll()
{
	if( _types.empty() )
	{
		return;
	}

	TypeListIter it = _types.begin();
	for( ; it != _types.end(); ++it )
	{
		lua_close( it->second->L );
		delete it->second;
	}
}